library(here)
source(here("utils/data_processing.R"))
source(here("utils/figures.R"))

Introduction

  • Goal is to model diagnoses from each criteria based on how frequently they co-occur together within a single 10-point differential diagnosis iteration.
  • The rationale is that an ideal set of criteria will tend to result in a relatively similar set of diagnoses in each iteration and thus a small set of highly co-occurring diagnoses.
  • Conversely, a poor set of criteria will generate highly variable diagnoses in each iteration and rates of co-occurrence will be comparatively lower.

Import and process data

df_gpt3.5 <- read_model("gpt-3.5-turbo-1106", icd = FALSE)
df_gpt4.0 <- read_model("gpt-4-turbo-preview", icd = FALSE)
df_claude3_haiku_t1.0 <- read_model("claude-3-haiku-20240307_t1-0", icd = FALSE)
df_claude3_opus_t1.0 <- read_model("claude-3-opus-20240229_t1-0", icd = FALSE)
df_gemini1.0_pro_t1.0 <- read_model("gemini-1.0-pro-002_t1-0", icd = FALSE)
df_gemini1.5_pro_t1.0 <- read_model("gemini-1.5-pro-001_t1-0", icd = FALSE)
df_gpt3.5_icd <- read_model("gpt-3.5-turbo-1106", icd = TRUE)
df_gpt4.0_icd <- read_model("gpt-4-turbo-preview", icd = TRUE)
df_claude3_haiku_t1.0_icd <- read_model("claude-3-haiku-20240307_t1-0", icd = TRUE)
df_claude3_opus_t1.0_icd <- read_model("claude-3-opus-20240229_t1-0", icd = TRUE)
df_gemini1.0_pro_t1.0_icd <- read_model("gemini-1.0-pro-002_t1-0", icd = TRUE)
df_gemini1.5_pro_t1.0_icd <- read_model("gemini-1.5-pro-001_t1-0", icd = TRUE)
# Tally each pair of co-occuring diagnoses within each criteria
# Original responses
df_gpt3.5_codiag <- create_codiagnosis_df(df_gpt3.5, remove_singletons = T)
df_gpt4.0_codiag <- create_codiagnosis_df(df_gpt4.0, remove_singletons = T)
df_claude3_haiku_t1.0_codiag <- create_codiagnosis_df(df_claude3_haiku_t1.0, remove_singletons = T)
df_claude3_opus_t1.0_codiag <- create_codiagnosis_df(df_claude3_opus_t1.0, remove_singletons = T)
df_gemini1.0_pro_t1.0_codiag <- create_codiagnosis_df(df_gemini1.0_pro_t1.0, remove_singletons = T)
df_gemini1.5_pro_t1.0_codiag <- create_codiagnosis_df(df_gemini1.5_pro_t1.0, remove_singletons = T)
# ICD converted responses
df_gpt3.5_icd_codiag <- create_codiagnosis_df(df_gpt3.5_icd, remove_singletons = T)
df_gpt4.0_icd_codiag <- create_codiagnosis_df(df_gpt4.0_icd, remove_singletons = T)
df_claude3_haiku_t1.0_icd_codiag <- create_codiagnosis_df(df_claude3_haiku_t1.0_icd, remove_singletons = T)
df_claude3_opus_t1.0_icd_codiag <- create_codiagnosis_df(df_claude3_opus_t1.0_icd, remove_singletons = T)
df_gemini1.0_pro_t1.0_icd_codiag <- create_codiagnosis_df(df_gemini1.0_pro_t1.0_icd, remove_singletons = T)
df_gemini1.5_pro_t1.0_icd_codiag <- create_codiagnosis_df(df_gemini1.5_pro_t1.0_icd, remove_singletons = T)
df_gpt4.0_codiag

Graph visualization

Exploring layouts

  • To determine clearest visualization of nodes and edges
# Selecting a layout
top_n <-  200
seed <- 1234

layouts <- c("fr", "dh", "kk", "stress", "graphopt")

graph_top_gpt4 <- make_codiagnosis_graph(df_gpt4.0_codiag, n_diagnoses = top_n)

for (l in layouts){
  set.seed(seed)
  plt <- centrality_graph(graph_top_gpt4, layout = l)
  plt <- plt + ggtitle("GPT4", subtitle = sprintf("Layout %s", l))
  print(plt)
}

Individual model data

Original responses

top_n <-  100
seed <- 321
graph_layout <- "stress"

codiag_graph_wrapper <- function(data){
  set.seed(seed)
  data <- make_codiagnosis_graph(data, n_diagnoses = top_n)
  centrality_graph(data, layout = graph_layout)
}

codiag_graph_wrapper(df_gpt3.5_codiag)+ ggtitle("ChatGPT 3.5", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gpt4.0_codiag)+ ggtitle("ChatGPT 4.0", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_haiku_t1.0_codiag)+ ggtitle("Claude 3 Haiku", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_opus_t1.0_codiag)+ ggtitle("Claude 3 Opus", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.0_pro_t1.0_codiag)+ ggtitle("Gemini 1.0 Pro", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.5_pro_t1.0_codiag)+ ggtitle("Gemini 1.5 Pro", subtitle = sprintf("Top %s", top_n))

ICD converted respones

codiag_graph_wrapper(df_gpt3.5_icd_codiag)+ ggtitle("ChatGPT 3.5 ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gpt4.0_icd_codiag)+ ggtitle("ChatGPT 4.0 ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_haiku_t1.0_icd_codiag)+ ggtitle("Claude 3 Haiku ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_claude3_opus_t1.0_icd_codiag)+ ggtitle("Claude 3 Opus ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.0_pro_t1.0_icd_codiag)+ ggtitle("Gemini 1.0 Pro ICD", subtitle = sprintf("Top %s", top_n))

codiag_graph_wrapper(df_gemini1.5_pro_t1.0_icd_codiag)+ ggtitle("Gemini 1.5 Pro ICD", subtitle = sprintf("Top %s", top_n))

Combined model data

Original responses

set.seed(seed)
multi_make_codiagnosis_graph(threshold_method = "individual", top_n = 100, layout="stress",
                             df_gpt3.5_codiag, df_gpt4.0_codiag, 
                             df_claude3_haiku_t1.0_codiag, df_claude3_opus_t1.0_codiag,
                             df_gemini1.0_pro_t1.0_codiag, df_gemini1.5_pro_t1.0_codiag)

multi_make_codiagnosis_graph(threshold_method = "average", top_n = 100, layout="stress",
                             df_gpt3.5_codiag, df_gpt4.0_codiag, 
                             df_claude3_haiku_t1.0_codiag, df_claude3_opus_t1.0_codiag,
                             df_gemini1.0_pro_t1.0_codiag, df_gemini1.5_pro_t1.0_codiag)

ICD converted responses

set.seed(seed)
multi_make_codiagnosis_graph(threshold_method = "individual", top_n = 100, layout="stress",
                             df_gpt3.5_icd_codiag, df_gpt4.0_icd_codiag, 
                             df_claude3_haiku_t1.0_icd_codiag, df_claude3_opus_t1.0_icd_codiag,
                             df_gemini1.0_pro_t1.0_icd_codiag, df_gemini1.5_pro_t1.0_icd_codiag)

multi_make_codiagnosis_graph(threshold_method = "average", top_n = 100, layout="stress",
                             df_gpt3.5_icd_codiag, df_gpt4.0_icd_codiag, 
                             df_claude3_haiku_t1.0_icd_codiag, df_claude3_opus_t1.0_icd_codiag,
                             df_gemini1.0_pro_t1.0_icd_codiag, df_gemini1.5_pro_t1.0_icd_codiag)

Evaluate edge density

  • Edge density represents the total number of edges in a graph relative to the total number of possible edges in the graph
    • When all possible edges are present, edge density = 1
    • When no edges are present, edge density = 0
  • Dense co-occurrence networks represent criteria that generate highly reproducible diagnoses. A sparse co-occurrence represents a high degree of variability

Combined model data

Original responses

multi_edge_density_plot(
  df_gpt3.5_codiag,
  df_gpt4.0_codiag,
  df_claude3_haiku_t1.0_codiag,
  df_claude3_opus_t1.0_codiag,
  df_gemini1.0_pro_t1.0_codiag,
  df_gemini1.5_pro_t1.0_codiag
)  
Warning: The `fun.y` argument of `stat_summary()` is deprecated as of ggplot2 3.3.0.
Please use the `fun` argument instead.Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

ICD converted responses

plt_edge_icd <- multi_edge_density_plot(
  df_gpt3.5_icd_codiag,
  df_gpt4.0_icd_codiag,
  df_claude3_haiku_t1.0_icd_codiag,
  df_claude3_opus_t1.0_icd_codiag,
  df_gemini1.0_pro_t1.0_icd_codiag,
  df_gemini1.5_pro_t1.0_icd_codiag
)  
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
plt_edge_icd

plt_edge_icd$data %>% summarise(mean(edge_density), .by="criteria")
extract_ggpubr_pvalues(plt_edge_icd)

Plot centrality

calculate_subgraph_centrality <- function(g, centrality_fun = "centrality_eigen"){
  data.frame(criteria = g %>% activate(edges) %>% pull(criteria) %>% unique()) %>% 
    mutate(sub_graphs = map(criteria, function(c){
      g %>% as_data_frame() %>% filter(criteria == c) %>% as_tbl_graph(directed = F) %>% 
        activate(nodes) %>% mutate(centrality = get(centrality_fun)()) %>% data.frame()
    })) %>% 
  unnest(sub_graphs) %>% 
  pivot_wider(names_from = "name", values_from = "centrality", values_fill = 0) %>% 
  column_to_rownames("criteria")
}

graph_all_gpt4 <- make_codiagnosis_graph(df_gpt4.0_codiag, n_diagnoses = top_n)
df_centr_gpt4 <- calculate_subgraph_centrality(graph_all_gpt4)
centrality_similarity <- function(data){
  data %>% 
    rownames_to_column("criteria") %>% 
    format_criteria() %>% 
    column_to_rownames("criteria") %>% 
    as.matrix() %>% 
    t() %>% 
    lsa::cosine() %>% 
    as.data.frame() %>% 
    rownames_to_column("V1") %>% 
    pivot_longer(-V1, names_to = "V2", values_to = "cosine")
}

centrality_similarity(df_centr_gpt4)
centrality_wrapper <- function(data, n_diag=NULL){
  make_codiagnosis_graph(data, n_diagnoses = n_diag) %>% 
    calculate_subgraph_centrality() %>% 
    centrality_similarity()
}

average_cosine_matrix <- listN(
  df_gpt3.5_icd_codiag,
  df_gpt4.0_icd_codiag,
  df_claude3_haiku_t1.0_icd_codiag,
  df_claude3_opus_t1.0_icd_codiag,
  df_gemini1.0_pro_t1.0_icd_codiag,
  df_gemini1.5_pro_t1.0_icd_codiag
) %>%
  enframe(name = "model", value = "data") %>%
  mutate(data = map(data, centrality_wrapper)) %>%
  unnest(data) %>%
  dplyr::summarise(cosine = mean(cosine), .by = c("V1", "V2")) %>%
  pivot_wider(names_from = "V2", values_from = "cosine") %>%
  column_to_rownames("V1") %>%
  as.matrix()

average_cosine_matrix
                   SLE - SLICC SLE - EULAR-ACR MCAS - Consortium MCAS - Alternative
SLE - SLICC          1.0000000       0.8540815         0.2276693          0.5103618
SLE - EULAR-ACR      0.8540815       1.0000000         0.2682776          0.4819197
MCAS - Consortium    0.2276693       0.2682776         1.0000000          0.5459547
MCAS - Alternative   0.5103618       0.4819197         0.5459547          1.0000000
custom_heatmap <- function(data, 
                           plot_title=NULL, 
                           legend_title=NULL, 
                           color_scale = NULL, 
                           midpoint = NULL, 
                           symmetric = T, 
                           matrix_title_size=10,
                           matrix_names_size=8,
                           legend_title_size=10,
                           legend_label_size=8,
                           dendrograms=T,
                           dendrogram_weight = unit(10, "mm"),
                           legend_orientation = "vertical",
                           legend_length=NULL,
                           grid_lines=F
                           ){
  
  # Determin midpoint of data
  scale_max <- ifelse(symmetric,max(abs(data)),max(data))
  scale_min <- ifelse(symmetric,-max(abs(data)),min(data))
  scale_mid <- scale_min + (scale_max - scale_min)/2
  
  midpoint <- ifelse(is.null(midpoint), scale_mid, midpoint)
  
  # Set default colorscales based on symmetry of data
  if (is.null(color_scale) & symmetric){color_scale <- hcl.colors(3, "Earth")}
  if (is.null(color_scale) & !symmetric){color_scale <- viridis::viridis(3)}
  color_function <-circlize::colorRamp2(c(scale_min,midpoint,scale_max), color_scale)
  
  # Legend parameters
  legend_params <- list(
      "title_gp" = grid::gpar(fontsize = legend_title_size, fontface = "bold"),
      "labels_gp" = grid::gpar(fontsize = legend_label_size),
      "direction" = legend_orientation
      )
  
  # Heatmap parameters
  heatmap_arguments <- list(
    "matrix" = data,
    "col" = color_function,
    "row_names_gp" = grid::gpar(fontsize = matrix_names_size),
    "column_names_gp" = grid::gpar(fontsize = matrix_names_size),
    "show_column_dend"=dendrograms,
    "show_row_dend"=dendrograms
  )
  
  if(!is.null(legend_title)){heatmap_arguments[["name"]] <- legend_title}
  if(grid_lines){heatmap_arguments[["rect_gp"]] <- grid::gpar(col = "black", lwd = 1)}
  if(dendrograms){
    heatmap_arguments[['column_dend_height']] <- dendrogram_weight
    heatmap_arguments[['row_dend_width']] <- dendrogram_weight
  }
  
  legend_side <- ifelse(legend_orientation=="vertical","right","bottom")
  if(legend_orientation=="vertical" &!is.null(legend_length)){
    legend_params[['legend_height']] <- legend_length}
  if(legend_orientation=="horizontal" &!is.null(legend_length)){
    legend_params[['legend_width']] <- legend_length}
  # Function call
  heatmap_arguments[["heatmap_legend_param"]] <- legend_params
  ht <- do.call(Heatmap, heatmap_arguments)
  draw(ht, heatmap_legend_side=legend_side, align_heatmap_legend = "global_center")
  
}

custom_heatmap(average_cosine_matrix, symmetric = F, legend_title = "Cosine similarity", grid_lines = T, dendrograms = F, legend_orientation = "horizontal", legend_length=unit(10, "cm"))

df_comp <- listN(
  df_gpt3.5_icd_codiag,
  df_gpt4.0_icd_codiag,
  df_claude3_haiku_t1.0_icd_codiag,
  df_claude3_opus_t1.0_icd_codiag,
  df_gemini1.0_pro_t1.0_icd_codiag,
  df_gemini1.5_pro_t1.0_icd_codiag
) %>%
  enframe(name = "model", value = "data") %>%
  mutate(data = map(data, centrality_wrapper)) %>%
  unnest(data)
cosine_similarity_compare <- function(df, pt_size=1,p_size=3){
Warning message:
In do_once((if (is_R_CMD_check()) stop else warning)("The function xfun::isFALSE() will be deprecated in the future. Please ",  :
  The function xfun::isFALSE() will be deprecated in the future. Please consider using base::isFALSE(x) or identical(x, FALSE) instead.
  # Format data
  df <- df %>% 
    unite(comp, V1, V2) %>% 
    filter(comp %in% c("MCAS - Consortium_MCAS - Alternative", "SLE - EULAR-ACR_SLE - SLICC")) %>% 
    mutate(comp = gsub("_", "\nvs. ", comp)) %>%  
    format_models() 
  
  # Plot data
  df %>% 
    ggplot(aes(x = comp, y = cosine, color = model))+
    geom_point(size=pt_size, position = position_dodge(width = 0.75))+
    theme_bw() +
    ggpubr::stat_compare_means(aes(group = comp), method = "wilcox.test", label = "p", vjust = 0.75, show.legend = F, size = p_size)+
    theme(axis.text.x = element_text(angle =90, hjust =1)) +
    labs(x=NULL, y="Cosine similarity") +
    scale_color_brewer(palette = "Dark2") +
    labs(color = "")
}
cosine_similarity_compare(df_comp)

cosine_similarity_compare(df_comp)

individual_cosine_matrix <- combine_data_frames(
  df_gpt3.5_icd_codiag,
  df_gpt4.0_icd_codiag,
  df_claude3_haiku_t1.0_icd_codiag,
  df_claude3_opus_t1.0_icd_codiag,
  df_gemini1.0_pro_t1.0_icd_codiag,
  df_gemini1.5_pro_t1.0_icd_codiag,
  additional_function = function(x){
    x %>% 
      make_codiagnosis_graph() %>% 
      calculate_subgraph_centrality() %>% 
      rownames_to_column("criteria") %>% 
      pivot_longer(-criteria, names_to="diagnosis", values_to="centrality")
  }
) %>% 
  unite(criteria, original_df, criteria, sep = "-") %>% 
  pivot_wider(names_from = "diagnosis", values_from="centrality", values_fill=0) %>% 
  column_to_rownames("criteria") %>% 
  as.matrix() %>% 
    t() %>% 
    lsa::cosine() %>% 
    as.data.frame() %>% 
    rownames_to_column("V1") %>% 
    pivot_longer(-V1, names_to = "V2", values_to = "cosine") %>% 
  pivot_wider(names_from = "V2", values_from = "cosine") %>%
  column_to_rownames("V1") %>%
  as.matrix()

individual_cosine_matrix[1:5, 1:5]
                                      df_gpt3.5_icd_codiag-slicc_sle df_gpt3.5_icd_codiag-eular_acr_sle df_gpt3.5_icd_codiag-mcas_consortium df_gpt3.5_icd_codiag-mcas_alternative
df_gpt3.5_icd_codiag-slicc_sle                             1.0000000                          0.8647922                            0.2136086                             0.4720033
df_gpt3.5_icd_codiag-eular_acr_sle                         0.8647922                          1.0000000                            0.2643814                             0.4613926
df_gpt3.5_icd_codiag-mcas_consortium                       0.2136086                          0.2643814                            1.0000000                             0.5519625
df_gpt3.5_icd_codiag-mcas_alternative                      0.4720033                          0.4613926                            0.5519625                             1.0000000
df_gpt4.0_icd_codiag-slicc_sle                             0.8522184                          0.7324445                            0.1663721                             0.3913603
                                      df_gpt4.0_icd_codiag-slicc_sle
df_gpt3.5_icd_codiag-slicc_sle                             0.8522184
df_gpt3.5_icd_codiag-eular_acr_sle                         0.7324445
df_gpt3.5_icd_codiag-mcas_consortium                       0.1663721
df_gpt3.5_icd_codiag-mcas_alternative                      0.3913603
df_gpt4.0_icd_codiag-slicc_sle                             1.0000000
alt_heatmap <- model_criteria_heatmap(individual_cosine_matrix, 
                color_scale = viridis::viridis(3), 
                title = " ", 
                metric = "Cosine\nsimilarity", 
                symmetric = F,
                font_size = 8)  
  

alt_heatmap

plt_alt <- cowplot::plot_grid(grid::grid.grabExpr(ComplexHeatmap::draw(alt_heatmap)))
plt_alt

ggsave(here("figures/4_alt_heatmap.pdf"), plot=plt_alt, height = 4, width = 6)

Final plot

Version 1

# n_diagnoses_bar <- 10
# n_diagnoses_abundance <- 50
# n_diagnoses_cumulative <- 50

title_size <- 9
label_size <- 6
legend_x_pad <- 0
legend_y_pad <- 0

apply_text_formatting <- list(
  theme(
    axis.text = element_text(size = label_size),
    axis.title = element_text(size = title_size),
    legend.text = element_text(size = label_size, margin=margin(0,0,0,r=1)),
    strip.text = element_text(size = label_size + 1),
    legend.key.height = unit(0.4, 'cm'),
    legend.key.width = unit(0.4, 'cm'),
    # legend.key = element_rect(size =  margin(0,0,0,0)),
    legend.box.background = element_rect(color = "black", size = 1),
    legend.margin = margin(
      t = legend_y_pad,
      r = legend_x_pad+2,
      b = legend_y_pad,
      l = legend_x_pad 
    ),
    legend.key.spacing.y = unit(-1.5, "pt"),
    legend.box.spacing = unit(1.0,"pt"),
    axis.text.x=element_text(angle=45,hjust=1)
  )
)


strip_margin <- 1
strip_formatting <- list(theme(
  strip.text.x = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin)), 
  strip.text.y = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
  # strip.background = element_rect(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
))

set.seed(1234)
plt_graph_icd <-
  multi_make_codiagnosis_graph(
    threshold_method = "average",
    top_n = 100,
    layout = "stress",
    df_gpt3.5_codiag,
    df_gpt4.0_codiag,
    df_claude3_haiku_t1.0_codiag,
    df_claude3_opus_t1.0_codiag,
    df_gemini1.0_pro_t1.0_codiag,
    df_gemini1.5_pro_t1.0_codiag,
    point_size = 1.25,
    border_size = 0.25,
    edge_width = 0.5,
    edge_alpha = 0.5,
    label_text_size = 9,
    tick_text_size = 6,
    highlight_stroke_multiplier = 3,
    legend_height = unit(25, "pt"),
    legend_width = unit(10, "pt")
  ) 

plt_edge_icd <- multi_edge_density_plot(
  df_gpt3.5_icd_codiag,
  df_gpt4.0_icd_codiag,
  df_claude3_haiku_t1.0_icd_codiag,
  df_claude3_opus_t1.0_icd_codiag,
  df_gemini1.0_pro_t1.0_icd_codiag,
  df_gemini1.5_pro_t1.0_icd_codiag
)  +
  apply_text_formatting +
  theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(override.aes = list(size = 1), ncol = 2))+
  scale_y_continuous(expand = expansion(mult = c(0.1,0.1)))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
plt_heatmap_icd <-
  custom_heatmap(
    average_cosine_matrix,
    symmetric = F,
    legend_title = "Cosine similarity",
    grid_lines = T,
    dendrograms = T,
    legend_orientation = "horizontal",
    legend_length = unit(2, "cm"),
    matrix_names_size = 6, 
    legend_title_size = 7.5,
    legend_label_size = 6,
    dendrogram_weight = unit(2, "mm")
  )



plt_fig <- cowplot::plot_grid(
  NULL, 
  cowplot::plot_grid(
    NULL, plt_graph_icd, nrow=1, rel_widths=c(0.05,0.9)
  ),
  NULL,
  cowplot::plot_grid(
    cowplot::plot_grid(NULL, plt_edge_icd ,rel_heights = c(0.1, 1), ncol = 1),
    NULL,
    cowplot::plot_grid(
      NULL,
      grid::grid.grabExpr(ComplexHeatmap::draw(plt_heatmap_icd, heatmap_legend_side = "bottom")),
      NULL,
      ncol = 1,
      rel_heights = c(0.1, 1, 0.1)
    ),
    nrow = 1,
    rel_widths = c(0.5, 0.1, 0.6),
    labels = c("B","","C"),
    axis = 'h', align = 'bt'
  ),

  ncol = 1,
  rel_heights = c(0.05, 0.95, 0.01, 1),
  labels = c("A")
)

plt_fig 

Version 2

# n_diagnoses_bar <- 10
# n_diagnoses_abundance <- 50
# n_diagnoses_cumulative <- 50

title_size <- 9
label_size <- 6
legend_x_pad <- 0
legend_y_pad <- 0

apply_text_formatting <- list(
  theme(
    axis.text = element_text(size = label_size),
    axis.title = element_text(size = title_size),
    legend.text = element_text(size = label_size, margin=margin(0,0,0,r=1)),
    strip.text = element_text(size = label_size + 1),
    legend.key.height = unit(0.4, 'cm'),
    legend.key.width = unit(0.4, 'cm'),
    # legend.key = element_rect(size =  margin(0,0,0,0)),
    legend.box.background = element_rect(color = "black", size = 1),
    legend.margin = margin(
      t = legend_y_pad,
      r = legend_x_pad+2,
      b = legend_y_pad,
      l = legend_x_pad 
    ),
    legend.key.spacing.y = unit(-1.5, "pt"),
    legend.box.spacing = unit(1.0,"pt"),
    axis.text.x=element_text(angle=90, vjust=0.5)
  )
)


strip_margin <- 1
strip_formatting <- list(theme(
  strip.text.x = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin)), 
  strip.text.y = element_text(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
  # strip.background = element_rect(margin = margin(t = strip_margin, r = strip_margin, b = strip_margin, l = strip_margin))
))

set.seed(1234)
plt_graph_icd <-
  multi_make_codiagnosis_graph(
    threshold_method = "average",
    top_n = 100,
    layout = "stress",
    df_gpt3.5_codiag,
    df_gpt4.0_codiag,
    df_claude3_haiku_t1.0_codiag,
    df_claude3_opus_t1.0_codiag,
    df_gemini1.0_pro_t1.0_codiag,
    df_gemini1.5_pro_t1.0_codiag,
    point_size = 1.25,
    border_size = 0.25,
    edge_width = 0.5,
    edge_alpha = 0.5,
    label_text_size = 9,
    tick_text_size = 6,
    highlight_stroke_multiplier = 3,
    legend_height = unit(25, "pt"),
    legend_width = unit(10, "pt")
  ) 

plt_edge_icd <- multi_edge_density_plot(
  df_gpt3.5_icd_codiag,
  df_gpt4.0_icd_codiag,
  df_claude3_haiku_t1.0_icd_codiag,
  df_claude3_opus_t1.0_icd_codiag,
  df_gemini1.0_pro_t1.0_icd_codiag,
  df_gemini1.5_pro_t1.0_icd_codiag
)  +
  apply_text_formatting +
  # theme(legend.position = "bottom", legend.direction = "horizontal") +
  guides(color = guide_legend(override.aes = list(size = 1), ncol = 2))+
  scale_y_continuous(expand = expansion(mult = c(0.1,0.1)))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
plt_cosine <- cosine_similarity_compare(df_comp, p_size = 2) +
  apply_text_formatting

plt_heatmap_icd <-
  custom_heatmap(
    average_cosine_matrix,
    symmetric = F,
    legend_title = "Average\ncosine similarity",
    grid_lines = T,
    dendrograms = T,
    legend_orientation = "horizontal",
    legend_length = unit(2.5, "cm"),
    matrix_names_size = 6, 
    legend_title_size = 7.5,
    legend_label_size = 6,
    dendrogram_weight = unit(2.5, "mm")
  )


pd <- -1

plt_fig <- cowplot::plot_grid(
  #1
  NULL, 
  #2
  cowplot::plot_grid(
    NULL, plt_graph_icd, nrow=1, rel_widths=c(0.05,0.9)
  ),
  #3
  NULL,
  #4
  cowplot::plot_grid(
    #4.1
    cowplot::plot_grid(
      #4.1.1
      plot_grid(
        #4.1.1.1
        plt_edge_icd+theme(legend.position = "none"),
        #4.1.1.2
        plt_cosine+theme(legend.position="none")+ylim(0.4,1), 
        # rel_widths = c(10,9),
        nrow=1,
        align="h",axis="bt"
        ),
      #4.1.2
      NA,
      plot_grid(NULL,get_legend(plt_edge_icd),nrow=1,rel_widths=c(0.15,1)),
      NA,
      ncol=1,
      rel_heights = c(1, 0.04, 0.1,0.1) #4.1._
    ),
    #4.2
    cowplot::plot_grid(
      #4.2.1
      NULL,
      #4.2.2
      grid::grid.grabExpr(ComplexHeatmap::draw(plt_heatmap_icd, heatmap_legend_side = "bottom", padding = unit(c(0,0,0,-1), "mm"))),
      #4.2.3
      NULL,
      ncol = 1,
      rel_heights = c(0.1, 1, 0.1)
    ),
    nrow = 1,
    rel_widths = c(0.6,  0.5), #4._
    align = 'h', axis = 'bt'
  ),

  ncol = 1,
  rel_heights = c(0.05, 0.95, 0.05, 1)
)
Warning: Multiple components found; returning the first one. To return all, use `return_all = TRUE`.Warning: Cannot convert object of class logical into a grob.Warning: Cannot convert object of class logical into a grob.
plt_fig <- cowplot::ggdraw(plt_fig)+cowplot::draw_plot_label(c("A","B","C","D"), x=c(0,0,0.25,0.52), y=c(1,rep(0.52,3)))
plt_fig 

ggsave(here("figures/4_Network_analysis.pdf"), plot=plt_fig, height = 5.5, width = 3.5)
LS0tCnRpdGxlOiAiQ28tZGlhZ25vc2lzIG5ldHdvcmsgYW5hbHlzaXMiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKYGBge3IsIG1lc3NhZ2UgPSBGfQpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlKCJ1dGlscy9kYXRhX3Byb2Nlc3NpbmcuUiIpKQpzb3VyY2UoaGVyZSgidXRpbHMvZmlndXJlcy5SIikpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCi0gR29hbCBpcyB0byBtb2RlbCBkaWFnbm9zZXMgZnJvbSBlYWNoIGNyaXRlcmlhIGJhc2VkIG9uIGhvdyBmcmVxdWVudGx5IHRoZXkgY28tb2NjdXIgdG9nZXRoZXIgd2l0aGluIGEgc2luZ2xlIDEwLXBvaW50IGRpZmZlcmVudGlhbCBkaWFnbm9zaXMgaXRlcmF0aW9uLgotIFRoZSByYXRpb25hbGUgaXMgdGhhdCBhbiBpZGVhbCBzZXQgb2YgY3JpdGVyaWEgd2lsbCB0ZW5kIHRvIHJlc3VsdCBpbiBhIHJlbGF0aXZlbHkgc2ltaWxhciBzZXQgb2YgZGlhZ25vc2VzIGluIGVhY2ggaXRlcmF0aW9uIGFuZCB0aHVzIGEgc21hbGwgc2V0IG9mIGhpZ2hseSBjby1vY2N1cnJpbmcgZGlhZ25vc2VzLgotIENvbnZlcnNlbHksIGEgcG9vciBzZXQgb2YgY3JpdGVyaWEgd2lsbCBnZW5lcmF0ZSBoaWdobHkgdmFyaWFibGUgZGlhZ25vc2VzIGluIGVhY2ggaXRlcmF0aW9uIGFuZCByYXRlcyBvZiBjby1vY2N1cnJlbmNlIHdpbGwgYmUgY29tcGFyYXRpdmVseSBsb3dlci4KCiMgSW1wb3J0IGFuZCBwcm9jZXNzIGRhdGEKCmBgYHtyLCBtZXNzYWdlID0gRn0KZGZfZ3B0My41IDwtIHJlYWRfbW9kZWwoImdwdC0zLjUtdHVyYm8tMTEwNiIsIGljZCA9IEZBTFNFKQpkZl9ncHQ0LjAgPC0gcmVhZF9tb2RlbCgiZ3B0LTQtdHVyYm8tcHJldmlldyIsIGljZCA9IEZBTFNFKQpkZl9jbGF1ZGUzX2hhaWt1X3QxLjAgPC0gcmVhZF9tb2RlbCgiY2xhdWRlLTMtaGFpa3UtMjAyNDAzMDdfdDEtMCIsIGljZCA9IEZBTFNFKQpkZl9jbGF1ZGUzX29wdXNfdDEuMCA8LSByZWFkX21vZGVsKCJjbGF1ZGUtMy1vcHVzLTIwMjQwMjI5X3QxLTAiLCBpY2QgPSBGQUxTRSkKZGZfZ2VtaW5pMS4wX3Byb190MS4wIDwtIHJlYWRfbW9kZWwoImdlbWluaS0xLjAtcHJvLTAwMl90MS0wIiwgaWNkID0gRkFMU0UpCmRmX2dlbWluaTEuNV9wcm9fdDEuMCA8LSByZWFkX21vZGVsKCJnZW1pbmktMS41LXByby0wMDFfdDEtMCIsIGljZCA9IEZBTFNFKQpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRn0KZGZfZ3B0My41X2ljZCA8LSByZWFkX21vZGVsKCJncHQtMy41LXR1cmJvLTExMDYiLCBpY2QgPSBUUlVFKQpkZl9ncHQ0LjBfaWNkIDwtIHJlYWRfbW9kZWwoImdwdC00LXR1cmJvLXByZXZpZXciLCBpY2QgPSBUUlVFKQpkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkIDwtIHJlYWRfbW9kZWwoImNsYXVkZS0zLWhhaWt1LTIwMjQwMzA3X3QxLTAiLCBpY2QgPSBUUlVFKQpkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2QgPC0gcmVhZF9tb2RlbCgiY2xhdWRlLTMtb3B1cy0yMDI0MDIyOV90MS0wIiwgaWNkID0gVFJVRSkKZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCA8LSByZWFkX21vZGVsKCJnZW1pbmktMS4wLXByby0wMDJfdDEtMCIsIGljZCA9IFRSVUUpCmRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2QgPC0gcmVhZF9tb2RlbCgiZ2VtaW5pLTEuNS1wcm8tMDAxX3QxLTAiLCBpY2QgPSBUUlVFKQpgYGAKCmBgYHtyfQojIFRhbGx5IGVhY2ggcGFpciBvZiBjby1vY2N1cmluZyBkaWFnbm9zZXMgd2l0aGluIGVhY2ggY3JpdGVyaWEKIyBPcmlnaW5hbCByZXNwb25zZXMKZGZfZ3B0My41X2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ3B0My41LCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2dwdDQuMF9jb2RpYWcgPC0gY3JlYXRlX2NvZGlhZ25vc2lzX2RmKGRmX2dwdDQuMCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9jbGF1ZGUzX2hhaWt1X3QxLjAsIHJlbW92ZV9zaW5nbGV0b25zID0gVCkKZGZfY2xhdWRlM19vcHVzX3QxLjBfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9jbGF1ZGUzX29wdXNfdDEuMCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9nZW1pbmkxLjBfcHJvX3QxLjBfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9nZW1pbmkxLjBfcHJvX3QxLjAsIHJlbW92ZV9zaW5nbGV0b25zID0gVCkKZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ2VtaW5pMS41X3Byb190MS4wLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmBgYAoKCmBgYHtyfQojIElDRCBjb252ZXJ0ZWQgcmVzcG9uc2VzCmRmX2dwdDMuNV9pY2RfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9ncHQzLjVfaWNkLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2dwdDQuMF9pY2RfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9ncHQ0LjBfaWNkLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2RfY29kaWFnIDwtIGNyZWF0ZV9jb2RpYWdub3Npc19kZihkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkLCByZW1vdmVfc2luZ2xldG9ucyA9IFQpCmRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcgPC0gY3JlYXRlX2NvZGlhZ25vc2lzX2RmKGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkX2NvZGlhZyA8LSBjcmVhdGVfY29kaWFnbm9zaXNfZGYoZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZCwgcmVtb3ZlX3NpbmdsZXRvbnMgPSBUKQpgYGAKCgpgYGB7cn0KZGZfZ3B0NC4wX2NvZGlhZwpgYGAKCiMgR3JhcGggdmlzdWFsaXphdGlvbgoKIyMgRXhwbG9yaW5nIGxheW91dHMgCgotIFRvIGRldGVybWluZSBjbGVhcmVzdCB2aXN1YWxpemF0aW9uIG9mIG5vZGVzIGFuZCBlZGdlcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQojIFNlbGVjdGluZyBhIGxheW91dAp0b3BfbiA8LSAgMjAwCnNlZWQgPC0gMTIzNAoKbGF5b3V0cyA8LSBjKCJmciIsICJkaCIsICJrayIsICJzdHJlc3MiLCAiZ3JhcGhvcHQiKQoKZ3JhcGhfdG9wX2dwdDQgPC0gbWFrZV9jb2RpYWdub3Npc19ncmFwaChkZl9ncHQ0LjBfY29kaWFnLCBuX2RpYWdub3NlcyA9IHRvcF9uKQoKZm9yIChsIGluIGxheW91dHMpewogIHNldC5zZWVkKHNlZWQpCiAgcGx0IDwtIGNlbnRyYWxpdHlfZ3JhcGgoZ3JhcGhfdG9wX2dwdDQsIGxheW91dCA9IGwpCiAgcGx0IDwtIHBsdCArIGdndGl0bGUoIkdQVDQiLCBzdWJ0aXRsZSA9IHNwcmludGYoIkxheW91dCAlcyIsIGwpKQogIHByaW50KHBsdCkKfQoKYGBgCiMjIEluZGl2aWR1YWwgbW9kZWwgZGF0YQoKIyMjIE9yaWdpbmFsIHJlc3BvbnNlcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQp0b3BfbiA8LSAgMTAwCnNlZWQgPC0gMzIxCmdyYXBoX2xheW91dCA8LSAic3RyZXNzIgoKY29kaWFnX2dyYXBoX3dyYXBwZXIgPC0gZnVuY3Rpb24oZGF0YSl7CiAgc2V0LnNlZWQoc2VlZCkKICBkYXRhIDwtIG1ha2VfY29kaWFnbm9zaXNfZ3JhcGgoZGF0YSwgbl9kaWFnbm9zZXMgPSB0b3BfbikKICBjZW50cmFsaXR5X2dyYXBoKGRhdGEsIGxheW91dCA9IGdyYXBoX2xheW91dCkKfQoKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ3B0My41X2NvZGlhZykrIGdndGl0bGUoIkNoYXRHUFQgMy41Iiwgc3VidGl0bGUgPSBzcHJpbnRmKCJUb3AgJXMiLCB0b3BfbikpCmNvZGlhZ19ncmFwaF93cmFwcGVyKGRmX2dwdDQuMF9jb2RpYWcpKyBnZ3RpdGxlKCJDaGF0R1BUIDQuMCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnKSsgZ2d0aXRsZSgiQ2xhdWRlIDMgSGFpa3UiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfY2xhdWRlM19vcHVzX3QxLjBfY29kaWFnKSsgZ2d0aXRsZSgiQ2xhdWRlIDMgT3B1cyIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9nZW1pbmkxLjBfcHJvX3QxLjBfY29kaWFnKSsgZ2d0aXRsZSgiR2VtaW5pIDEuMCBQcm8iLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZykrIGdndGl0bGUoIkdlbWluaSAxLjUgUHJvIiwgc3VidGl0bGUgPSBzcHJpbnRmKCJUb3AgJXMiLCB0b3BfbikpCmBgYAojIyMgSUNEIGNvbnZlcnRlZCByZXNwb25lcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9ncHQzLjVfaWNkX2NvZGlhZykrIGdndGl0bGUoIkNoYXRHUFQgMy41IElDRCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9ncHQ0LjBfaWNkX2NvZGlhZykrIGdndGl0bGUoIkNoYXRHUFQgNC4wIElDRCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZykrIGdndGl0bGUoIkNsYXVkZSAzIEhhaWt1IElDRCIsIHN1YnRpdGxlID0gc3ByaW50ZigiVG9wICVzIiwgdG9wX24pKQpjb2RpYWdfZ3JhcGhfd3JhcHBlcihkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2RfY29kaWFnKSsgZ2d0aXRsZSgiQ2xhdWRlIDMgT3B1cyBJQ0QiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcpKyBnZ3RpdGxlKCJHZW1pbmkgMS4wIFBybyBJQ0QiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKY29kaWFnX2dyYXBoX3dyYXBwZXIoZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcpKyBnZ3RpdGxlKCJHZW1pbmkgMS41IFBybyBJQ0QiLCBzdWJ0aXRsZSA9IHNwcmludGYoIlRvcCAlcyIsIHRvcF9uKSkKYGBgCgojIyBDb21iaW5lZCBtb2RlbCBkYXRhCgojIyMgT3JpZ2luYWwgcmVzcG9uc2VzCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Niwgd2FybmluZz1GLCBtZXNzYWdlPUZ9CnNldC5zZWVkKHNlZWQpCm11bHRpX21ha2VfY29kaWFnbm9zaXNfZ3JhcGgodGhyZXNob2xkX21ldGhvZCA9ICJpbmRpdmlkdWFsIiwgdG9wX24gPSAxMDAsIGxheW91dD0ic3RyZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9ncHQzLjVfY29kaWFnLCBkZl9ncHQ0LjBfY29kaWFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnLCBkZl9jbGF1ZGUzX29wdXNfdDEuMF9jb2RpYWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywgZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZykKYGBgCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Niwgd2FybmluZz1GLCBtZXNzYWdlPUZ9Cm11bHRpX21ha2VfY29kaWFnbm9zaXNfZ3JhcGgodGhyZXNob2xkX21ldGhvZCA9ICJhdmVyYWdlIiwgdG9wX24gPSAxMDAsIGxheW91dD0ic3RyZXNzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9ncHQzLjVfY29kaWFnLCBkZl9ncHQ0LjBfY29kaWFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnLCBkZl9jbGF1ZGUzX29wdXNfdDEuMF9jb2RpYWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywgZGZfZ2VtaW5pMS41X3Byb190MS4wX2NvZGlhZykKYGBgCiMjIyBJQ0QgY29udmVydGVkIHJlc3BvbnNlcwoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTYsIHdhcm5pbmc9RiwgbWVzc2FnZT1GfQpzZXQuc2VlZChzZWVkKQptdWx0aV9tYWtlX2NvZGlhZ25vc2lzX2dyYXBoKHRocmVzaG9sZF9tZXRob2QgPSAiaW5kaXZpZHVhbCIsIHRvcF9uID0gMTAwLCBsYXlvdXQ9InN0cmVzcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ3B0My41X2ljZF9jb2RpYWcsIGRmX2dwdDQuMF9pY2RfY29kaWFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZywgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkX2NvZGlhZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZywgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD02LCB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0KbXVsdGlfbWFrZV9jb2RpYWdub3Npc19ncmFwaCh0aHJlc2hvbGRfbWV0aG9kID0gImF2ZXJhZ2UiLCB0b3BfbiA9IDEwMCwgbGF5b3V0PSJzdHJlc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRmX2dwdDMuNV9pY2RfY29kaWFnLCBkZl9ncHQ0LjBfaWNkX2NvZGlhZywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZF9jb2RpYWcsIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcsIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2RfY29kaWFnKQpgYGAKCiMgRXZhbHVhdGUgZWRnZSBkZW5zaXR5CgotIEVkZ2UgZGVuc2l0eSByZXByZXNlbnRzIHRoZSB0b3RhbCBudW1iZXIgb2YgZWRnZXMgaW4gYSBncmFwaCByZWxhdGl2ZSB0byB0aGUgdG90YWwgbnVtYmVyIG9mICoqcG9zc2libGUqKiBlZGdlcyBpbiB0aGUgZ3JhcGgKICAtIFdoZW4gYWxsIHBvc3NpYmxlIGVkZ2VzIGFyZSBwcmVzZW50LCBlZGdlIGRlbnNpdHkgPSAxCiAgLSBXaGVuIG5vIGVkZ2VzIGFyZSBwcmVzZW50LCBlZGdlIGRlbnNpdHkgPSAwCi0gRGVuc2UgY28tb2NjdXJyZW5jZSBuZXR3b3JrcyByZXByZXNlbnQgY3JpdGVyaWEgdGhhdCBnZW5lcmF0ZSBoaWdobHkgcmVwcm9kdWNpYmxlIGRpYWdub3Nlcy4gQSBzcGFyc2UgY28tb2NjdXJyZW5jZSByZXByZXNlbnRzIGEgaGlnaCBkZWdyZWUgb2YgdmFyaWFiaWxpdHkgCgojIyBDb21iaW5lZCBtb2RlbCBkYXRhCgojIyMgT3JpZ2luYWwgcmVzcG9uc2VzCgpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9My41fQptdWx0aV9lZGdlX2RlbnNpdHlfcGxvdCgKICBkZl9ncHQzLjVfY29kaWFnLAogIGRmX2dwdDQuMF9jb2RpYWcsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2NvZGlhZywKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfY29kaWFnCikgIAoKYGBgCgojIyMgSUNEIGNvbnZlcnRlZCByZXNwb25zZXMKCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD0zLjV9CnBsdF9lZGdlX2ljZCA8LSBtdWx0aV9lZGdlX2RlbnNpdHlfcGxvdCgKICBkZl9ncHQzLjVfaWNkX2NvZGlhZywKICBkZl9ncHQ0LjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2RfY29kaWFnCikgIApwbHRfZWRnZV9pY2QKcGx0X2VkZ2VfaWNkJGRhdGEgJT4lIHN1bW1hcmlzZShtZWFuKGVkZ2VfZGVuc2l0eSksIC5ieT0iY3JpdGVyaWEiKQpleHRyYWN0X2dncHVicl9wdmFsdWVzKHBsdF9lZGdlX2ljZCkKYGBgCgoKCiMgUGxvdCBjZW50cmFsaXR5CmBgYHtyfQpjYWxjdWxhdGVfc3ViZ3JhcGhfY2VudHJhbGl0eSA8LSBmdW5jdGlvbihnLCBjZW50cmFsaXR5X2Z1biA9ICJjZW50cmFsaXR5X2VpZ2VuIil7CiAgZGF0YS5mcmFtZShjcml0ZXJpYSA9IGcgJT4lIGFjdGl2YXRlKGVkZ2VzKSAlPiUgcHVsbChjcml0ZXJpYSkgJT4lIHVuaXF1ZSgpKSAlPiUgCiAgICBtdXRhdGUoc3ViX2dyYXBocyA9IG1hcChjcml0ZXJpYSwgZnVuY3Rpb24oYyl7CiAgICAgIGcgJT4lIGFzX2RhdGFfZnJhbWUoKSAlPiUgZmlsdGVyKGNyaXRlcmlhID09IGMpICU+JSBhc190YmxfZ3JhcGgoZGlyZWN0ZWQgPSBGKSAlPiUgCiAgICAgICAgYWN0aXZhdGUobm9kZXMpICU+JSBtdXRhdGUoY2VudHJhbGl0eSA9IGdldChjZW50cmFsaXR5X2Z1bikoKSkgJT4lIGRhdGEuZnJhbWUoKQogICAgfSkpICU+JSAKICB1bm5lc3Qoc3ViX2dyYXBocykgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAibmFtZSIsIHZhbHVlc19mcm9tID0gImNlbnRyYWxpdHkiLCB2YWx1ZXNfZmlsbCA9IDApICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoImNyaXRlcmlhIikKfQoKZ3JhcGhfYWxsX2dwdDQgPC0gbWFrZV9jb2RpYWdub3Npc19ncmFwaChkZl9ncHQ0LjBfY29kaWFnLCBuX2RpYWdub3NlcyA9IHRvcF9uKQpkZl9jZW50cl9ncHQ0IDwtIGNhbGN1bGF0ZV9zdWJncmFwaF9jZW50cmFsaXR5KGdyYXBoX2FsbF9ncHQ0KQpgYGAKCmBgYHtyfQpjZW50cmFsaXR5X3NpbWlsYXJpdHkgPC0gZnVuY3Rpb24oZGF0YSl7CiAgZGF0YSAlPiUgCiAgICByb3duYW1lc190b19jb2x1bW4oImNyaXRlcmlhIikgJT4lIAogICAgZm9ybWF0X2NyaXRlcmlhKCkgJT4lIAogICAgY29sdW1uX3RvX3Jvd25hbWVzKCJjcml0ZXJpYSIpICU+JSAKICAgIGFzLm1hdHJpeCgpICU+JSAKICAgIHQoKSAlPiUgCiAgICBsc2E6OmNvc2luZSgpICU+JSAKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICByb3duYW1lc190b19jb2x1bW4oIlYxIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKC1WMSwgbmFtZXNfdG8gPSAiVjIiLCB2YWx1ZXNfdG8gPSAiY29zaW5lIikKfQoKY2VudHJhbGl0eV9zaW1pbGFyaXR5KGRmX2NlbnRyX2dwdDQpCmBgYApgYGB7cn0KY2VudHJhbGl0eV93cmFwcGVyIDwtIGZ1bmN0aW9uKGRhdGEsIG5fZGlhZz1OVUxMKXsKICBtYWtlX2NvZGlhZ25vc2lzX2dyYXBoKGRhdGEsIG5fZGlhZ25vc2VzID0gbl9kaWFnKSAlPiUgCiAgICBjYWxjdWxhdGVfc3ViZ3JhcGhfY2VudHJhbGl0eSgpICU+JSAKICAgIGNlbnRyYWxpdHlfc2ltaWxhcml0eSgpCn0KCmF2ZXJhZ2VfY29zaW5lX21hdHJpeCA8LSBsaXN0TigKICBkZl9ncHQzLjVfaWNkX2NvZGlhZywKICBkZl9ncHQ0LjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfaWNkX2NvZGlhZywKICBkZl9jbGF1ZGUzX29wdXNfdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuMF9wcm9fdDEuMF9pY2RfY29kaWFnLAogIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9pY2RfY29kaWFnCikgJT4lCiAgZW5mcmFtZShuYW1lID0gIm1vZGVsIiwgdmFsdWUgPSAiZGF0YSIpICU+JQogIG11dGF0ZShkYXRhID0gbWFwKGRhdGEsIGNlbnRyYWxpdHlfd3JhcHBlcikpICU+JQogIHVubmVzdChkYXRhKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvc2luZSA9IG1lYW4oY29zaW5lKSwgLmJ5ID0gYygiVjEiLCAiVjIiKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJWMiIsIHZhbHVlc19mcm9tID0gImNvc2luZSIpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygiVjEiKSAlPiUKICBhcy5tYXRyaXgoKQoKYXZlcmFnZV9jb3NpbmVfbWF0cml4CgoKYGBgCgoKCmBgYHtyLCBmaWcud2lkdGg9NS41LCBmaWcuaGVpZ2h0PTMuNX0KY3VzdG9tX2hlYXRtYXAgPC0gZnVuY3Rpb24oZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGU9TlVMTCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF90aXRsZT1OVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3Jfc2NhbGUgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSBOVUxMLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ltbWV0cmljID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hdHJpeF90aXRsZV9zaXplPTEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRyaXhfbmFtZXNfc2l6ZT04LAogICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfdGl0bGVfc2l6ZT0xMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xhYmVsX3NpemU9OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuZHJvZ3JhbXM9VCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVuZHJvZ3JhbV93ZWlnaHQgPSB1bml0KDEwLCAibW0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX29yaWVudGF0aW9uID0gInZlcnRpY2FsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xlbmd0aD1OVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkX2xpbmVzPUYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKXsKICAKICAjIERldGVybWluIG1pZHBvaW50IG9mIGRhdGEKICBzY2FsZV9tYXggPC0gaWZlbHNlKHN5bW1ldHJpYyxtYXgoYWJzKGRhdGEpKSxtYXgoZGF0YSkpCiAgc2NhbGVfbWluIDwtIGlmZWxzZShzeW1tZXRyaWMsLW1heChhYnMoZGF0YSkpLG1pbihkYXRhKSkKICBzY2FsZV9taWQgPC0gc2NhbGVfbWluICsgKHNjYWxlX21heCAtIHNjYWxlX21pbikvMgogIAogIG1pZHBvaW50IDwtIGlmZWxzZShpcy5udWxsKG1pZHBvaW50KSwgc2NhbGVfbWlkLCBtaWRwb2ludCkKICAKICAjIFNldCBkZWZhdWx0IGNvbG9yc2NhbGVzIGJhc2VkIG9uIHN5bW1ldHJ5IG9mIGRhdGEKICBpZiAoaXMubnVsbChjb2xvcl9zY2FsZSkgJiBzeW1tZXRyaWMpe2NvbG9yX3NjYWxlIDwtIGhjbC5jb2xvcnMoMywgIkVhcnRoIil9CiAgaWYgKGlzLm51bGwoY29sb3Jfc2NhbGUpICYgIXN5bW1ldHJpYyl7Y29sb3Jfc2NhbGUgPC0gdmlyaWRpczo6dmlyaWRpcygzKX0KICBjb2xvcl9mdW5jdGlvbiA8LWNpcmNsaXplOjpjb2xvclJhbXAyKGMoc2NhbGVfbWluLG1pZHBvaW50LHNjYWxlX21heCksIGNvbG9yX3NjYWxlKQogIAogICMgTGVnZW5kIHBhcmFtZXRlcnMKICBsZWdlbmRfcGFyYW1zIDwtIGxpc3QoCiAgICAgICJ0aXRsZV9ncCIgPSBncmlkOjpncGFyKGZvbnRzaXplID0gbGVnZW5kX3RpdGxlX3NpemUsIGZvbnRmYWNlID0gImJvbGQiKSwKICAgICAgImxhYmVsc19ncCIgPSBncmlkOjpncGFyKGZvbnRzaXplID0gbGVnZW5kX2xhYmVsX3NpemUpLAogICAgICAiZGlyZWN0aW9uIiA9IGxlZ2VuZF9vcmllbnRhdGlvbgogICAgICApCiAgCiAgIyBIZWF0bWFwIHBhcmFtZXRlcnMKICBoZWF0bWFwX2FyZ3VtZW50cyA8LSBsaXN0KAogICAgIm1hdHJpeCIgPSBkYXRhLAogICAgImNvbCIgPSBjb2xvcl9mdW5jdGlvbiwKICAgICJyb3dfbmFtZXNfZ3AiID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IG1hdHJpeF9uYW1lc19zaXplKSwKICAgICJjb2x1bW5fbmFtZXNfZ3AiID0gZ3JpZDo6Z3Bhcihmb250c2l6ZSA9IG1hdHJpeF9uYW1lc19zaXplKSwKICAgICJzaG93X2NvbHVtbl9kZW5kIj1kZW5kcm9ncmFtcywKICAgICJzaG93X3Jvd19kZW5kIj1kZW5kcm9ncmFtcwogICkKICAKICBpZighaXMubnVsbChsZWdlbmRfdGl0bGUpKXtoZWF0bWFwX2FyZ3VtZW50c1tbIm5hbWUiXV0gPC0gbGVnZW5kX3RpdGxlfQogIGlmKGdyaWRfbGluZXMpe2hlYXRtYXBfYXJndW1lbnRzW1sicmVjdF9ncCJdXSA8LSBncmlkOjpncGFyKGNvbCA9ICJibGFjayIsIGx3ZCA9IDEpfQogIGlmKGRlbmRyb2dyYW1zKXsKICAgIGhlYXRtYXBfYXJndW1lbnRzW1snY29sdW1uX2RlbmRfaGVpZ2h0J11dIDwtIGRlbmRyb2dyYW1fd2VpZ2h0CiAgICBoZWF0bWFwX2FyZ3VtZW50c1tbJ3Jvd19kZW5kX3dpZHRoJ11dIDwtIGRlbmRyb2dyYW1fd2VpZ2h0CiAgfQogIAogIGxlZ2VuZF9zaWRlIDwtIGlmZWxzZShsZWdlbmRfb3JpZW50YXRpb249PSJ2ZXJ0aWNhbCIsInJpZ2h0IiwiYm90dG9tIikKICBpZihsZWdlbmRfb3JpZW50YXRpb249PSJ2ZXJ0aWNhbCIgJiFpcy5udWxsKGxlZ2VuZF9sZW5ndGgpKXsKICAgIGxlZ2VuZF9wYXJhbXNbWydsZWdlbmRfaGVpZ2h0J11dIDwtIGxlZ2VuZF9sZW5ndGh9CiAgaWYobGVnZW5kX29yaWVudGF0aW9uPT0iaG9yaXpvbnRhbCIgJiFpcy5udWxsKGxlZ2VuZF9sZW5ndGgpKXsKICAgIGxlZ2VuZF9wYXJhbXNbWydsZWdlbmRfd2lkdGgnXV0gPC0gbGVnZW5kX2xlbmd0aH0KICAjIEZ1bmN0aW9uIGNhbGwKICBoZWF0bWFwX2FyZ3VtZW50c1tbImhlYXRtYXBfbGVnZW5kX3BhcmFtIl1dIDwtIGxlZ2VuZF9wYXJhbXMKICBodCA8LSBkby5jYWxsKEhlYXRtYXAsIGhlYXRtYXBfYXJndW1lbnRzKQogIGRyYXcoaHQsIGhlYXRtYXBfbGVnZW5kX3NpZGU9bGVnZW5kX3NpZGUsIGFsaWduX2hlYXRtYXBfbGVnZW5kID0gImdsb2JhbF9jZW50ZXIiKQogIAp9CgpjdXN0b21faGVhdG1hcChhdmVyYWdlX2Nvc2luZV9tYXRyaXgsIHN5bW1ldHJpYyA9IEYsIGxlZ2VuZF90aXRsZSA9ICJDb3NpbmUgc2ltaWxhcml0eSIsIGdyaWRfbGluZXMgPSBULCBkZW5kcm9ncmFtcyA9IEYsIGxlZ2VuZF9vcmllbnRhdGlvbiA9ICJob3Jpem9udGFsIiwgbGVnZW5kX2xlbmd0aD11bml0KDEwLCAiY20iKSkKYGBgCmBgYHtyfQpkZl9jb21wIDwtIGxpc3ROKAogIGRmX2dwdDMuNV9pY2RfY29kaWFnLAogIGRmX2dwdDQuMF9pY2RfY29kaWFnLAogIGRmX2NsYXVkZTNfaGFpa3VfdDEuMF9pY2RfY29kaWFnLAogIGRmX2NsYXVkZTNfb3B1c190MS4wX2ljZF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2ljZF9jb2RpYWcsCiAgZGZfZ2VtaW5pMS41X3Byb190MS4wX2ljZF9jb2RpYWcKKSAlPiUKICBlbmZyYW1lKG5hbWUgPSAibW9kZWwiLCB2YWx1ZSA9ICJkYXRhIikgJT4lCiAgbXV0YXRlKGRhdGEgPSBtYXAoZGF0YSwgY2VudHJhbGl0eV93cmFwcGVyKSkgJT4lCiAgdW5uZXN0KGRhdGEpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0zLjUsIGZpZy5oZWlnaHQ9NH0KY29zaW5lX3NpbWlsYXJpdHlfY29tcGFyZSA8LSBmdW5jdGlvbihkZiwgcHRfc2l6ZT0xLHBfc2l6ZT0zKXsKICAjIEZvcm1hdCBkYXRhCiAgZGYgPC0gZGYgJT4lIAogICAgdW5pdGUoY29tcCwgVjEsIFYyKSAlPiUgCiAgICBmaWx0ZXIoY29tcCAlaW4lIGMoIk1DQVMgLSBDb25zb3J0aXVtX01DQVMgLSBBbHRlcm5hdGl2ZSIsICJTTEUgLSBFVUxBUi1BQ1JfU0xFIC0gU0xJQ0MiKSkgJT4lIAogICAgbXV0YXRlKGNvbXAgPSBnc3ViKCJfIiwgIlxudnMuICIsIGNvbXApKSAlPiUgIAogICAgZm9ybWF0X21vZGVscygpIAogIAogICMgUGxvdCBkYXRhCiAgZGYgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gY29tcCwgeSA9IGNvc2luZSwgY29sb3IgPSBtb2RlbCkpKwogICAgZ2VvbV9wb2ludChzaXplPXB0X3NpemUsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjc1KSkrCiAgICB0aGVtZV9idygpICsKICAgIGdncHVicjo6c3RhdF9jb21wYXJlX21lYW5zKGFlcyhncm91cCA9IGNvbXApLCBtZXRob2QgPSAid2lsY294LnRlc3QiLCBsYWJlbCA9ICJwIiwgdmp1c3QgPSAwLjc1LCBzaG93LmxlZ2VuZCA9IEYsIHNpemUgPSBwX3NpemUpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPTkwLCBoanVzdCA9MSkpICsKICAgIGxhYnMoeD1OVUxMLCB5PSJDb3NpbmUgc2ltaWxhcml0eSIpICsKICAgIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIkRhcmsyIikgKwogICAgbGFicyhjb2xvciA9ICIiKQp9CmNvc2luZV9zaW1pbGFyaXR5X2NvbXBhcmUoZGZfY29tcCkKYGBgCgpgYGB7cn0KaW5kaXZpZHVhbF9jb3NpbmVfbWF0cml4IDwtIGNvbWJpbmVfZGF0YV9mcmFtZXMoCiAgZGZfZ3B0My41X2ljZF9jb2RpYWcsCiAgZGZfZ3B0NC4wX2ljZF9jb2RpYWcsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZF9jb2RpYWcsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkX2NvZGlhZywKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZywKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkX2NvZGlhZywKICBhZGRpdGlvbmFsX2Z1bmN0aW9uID0gZnVuY3Rpb24oeCl7CiAgICB4ICU+JSAKICAgICAgbWFrZV9jb2RpYWdub3Npc19ncmFwaCgpICU+JSAKICAgICAgY2FsY3VsYXRlX3N1YmdyYXBoX2NlbnRyYWxpdHkoKSAlPiUgCiAgICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY3JpdGVyaWEiKSAlPiUgCiAgICAgIHBpdm90X2xvbmdlcigtY3JpdGVyaWEsIG5hbWVzX3RvPSJkaWFnbm9zaXMiLCB2YWx1ZXNfdG89ImNlbnRyYWxpdHkiKQogIH0KKSAlPiUgCiAgdW5pdGUoY3JpdGVyaWEsIG9yaWdpbmFsX2RmLCBjcml0ZXJpYSwgc2VwID0gIi0iKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJkaWFnbm9zaXMiLCB2YWx1ZXNfZnJvbT0iY2VudHJhbGl0eSIsIHZhbHVlc19maWxsPTApICU+JSAKICBjb2x1bW5fdG9fcm93bmFtZXMoImNyaXRlcmlhIikgJT4lIAogIGFzLm1hdHJpeCgpICU+JSAKICAgIHQoKSAlPiUgCiAgICBsc2E6OmNvc2luZSgpICU+JSAKICAgIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgICByb3duYW1lc190b19jb2x1bW4oIlYxIikgJT4lIAogICAgcGl2b3RfbG9uZ2VyKC1WMSwgbmFtZXNfdG8gPSAiVjIiLCB2YWx1ZXNfdG8gPSAiY29zaW5lIikgJT4lIAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSAiVjIiLCB2YWx1ZXNfZnJvbSA9ICJjb3NpbmUiKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoIlYxIikgJT4lCiAgYXMubWF0cml4KCkKCmluZGl2aWR1YWxfY29zaW5lX21hdHJpeFsxOjUsIDE6NV0KYGBgCgpgYGB7ciwgZmlnLndpZHRoPTUuNSwgZmlnLmhlaWdodD0zLjV9CmFsdF9oZWF0bWFwIDwtIG1vZGVsX2NyaXRlcmlhX2hlYXRtYXAoaW5kaXZpZHVhbF9jb3NpbmVfbWF0cml4LCAKICAgICAgICAgICAgICAgIGNvbG9yX3NjYWxlID0gdmlyaWRpczo6dmlyaWRpcygzKSwgCiAgICAgICAgICAgICAgICB0aXRsZSA9ICIgIiwgCiAgICAgICAgICAgICAgICBtZXRyaWMgPSAiQ29zaW5lXG5zaW1pbGFyaXR5IiwgCiAgICAgICAgICAgICAgICBzeW1tZXRyaWMgPSBGLAogICAgICAgICAgICAgICAgZm9udF9zaXplID0gOCkgIAogIAoKYWx0X2hlYXRtYXAKYGBgCmBgYHtyLCBmaWcud2lkdGg9NiwgZmlnLmhlaWdodD00fQpwbHRfYWx0IDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZChncmlkOjpncmlkLmdyYWJFeHByKENvbXBsZXhIZWF0bWFwOjpkcmF3KGFsdF9oZWF0bWFwKSkpCnBsdF9hbHQKYGBgCmBgYHtyfQpnZ3NhdmUoaGVyZSgiZmlndXJlcy80X2FsdF9oZWF0bWFwLnBkZiIpLCBwbG90PXBsdF9hbHQsIGhlaWdodCA9IDQsIHdpZHRoID0gNikKYGBgCgojIEZpbmFsIHBsb3QKCiMjIyBWZXJzaW9uIDEKYGBge3IsIGZpZy53aWR0aD0zLjUsIGZpZy5oZWlnaHQ9NS41fQojIG5fZGlhZ25vc2VzX2JhciA8LSAxMAojIG5fZGlhZ25vc2VzX2FidW5kYW5jZSA8LSA1MAojIG5fZGlhZ25vc2VzX2N1bXVsYXRpdmUgPC0gNTAKCnRpdGxlX3NpemUgPC0gOQpsYWJlbF9zaXplIDwtIDYKbGVnZW5kX3hfcGFkIDwtIDAKbGVnZW5kX3lfcGFkIDwtIDAKCmFwcGx5X3RleHRfZm9ybWF0dGluZyA8LSBsaXN0KAogIHRoZW1lKAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHRpdGxlX3NpemUpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUsIG1hcmdpbj1tYXJnaW4oMCwwLDAscj0xKSksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplICsgMSksCiAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMC40LCAnY20nKSwKICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuNCwgJ2NtJyksCiAgICAjIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3Qoc2l6ZSA9ICBtYXJnaW4oMCwwLDAsMCkpLAogICAgbGVnZW5kLmJveC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEpLAogICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigKICAgICAgdCA9IGxlZ2VuZF95X3BhZCwKICAgICAgciA9IGxlZ2VuZF94X3BhZCsyLAogICAgICBiID0gbGVnZW5kX3lfcGFkLAogICAgICBsID0gbGVnZW5kX3hfcGFkIAogICAgKSwKICAgIGxlZ2VuZC5rZXkuc3BhY2luZy55ID0gdW5pdCgtMS41LCAicHQiKSwKICAgIGxlZ2VuZC5ib3guc3BhY2luZyA9IHVuaXQoMS4wLCJwdCIpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LGhqdXN0PTEpCiAgKQopCgoKc3RyaXBfbWFyZ2luIDwtIDEKc3RyaXBfZm9ybWF0dGluZyA8LSBsaXN0KHRoZW1lKAogIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpLCAKICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKQogICMgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpCikpCgpzZXQuc2VlZCgxMjM0KQpwbHRfZ3JhcGhfaWNkIDwtCiAgbXVsdGlfbWFrZV9jb2RpYWdub3Npc19ncmFwaCgKICAgIHRocmVzaG9sZF9tZXRob2QgPSAiYXZlcmFnZSIsCiAgICB0b3BfbiA9IDEwMCwKICAgIGxheW91dCA9ICJzdHJlc3MiLAogICAgZGZfZ3B0My41X2NvZGlhZywKICAgIGRmX2dwdDQuMF9jb2RpYWcsCiAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnLAogICAgZGZfY2xhdWRlM19vcHVzX3QxLjBfY29kaWFnLAogICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywKICAgIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9jb2RpYWcsCiAgICBwb2ludF9zaXplID0gMS4yNSwKICAgIGJvcmRlcl9zaXplID0gMC4yNSwKICAgIGVkZ2Vfd2lkdGggPSAwLjUsCiAgICBlZGdlX2FscGhhID0gMC41LAogICAgbGFiZWxfdGV4dF9zaXplID0gOSwKICAgIHRpY2tfdGV4dF9zaXplID0gNiwKICAgIGhpZ2hsaWdodF9zdHJva2VfbXVsdGlwbGllciA9IDMsCiAgICBsZWdlbmRfaGVpZ2h0ID0gdW5pdCgyNSwgInB0IiksCiAgICBsZWdlbmRfd2lkdGggPSB1bml0KDEwLCAicHQiKQogICkgCgpwbHRfZWRnZV9pY2QgPC0gbXVsdGlfZWRnZV9kZW5zaXR5X3Bsb3QoCiAgZGZfZ3B0My41X2ljZF9jb2RpYWcsCiAgZGZfZ3B0NC4wX2ljZF9jb2RpYWcsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZF9jb2RpYWcsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkX2NvZGlhZywKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZywKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkX2NvZGlhZwopICArCiAgYXBwbHlfdGV4dF9mb3JtYXR0aW5nICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSAxKSwgbmNvbCA9IDIpKSsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMSwwLjEpKSkKCnBsdF9oZWF0bWFwX2ljZCA8LQogIGN1c3RvbV9oZWF0bWFwKAogICAgYXZlcmFnZV9jb3NpbmVfbWF0cml4LAogICAgc3ltbWV0cmljID0gRiwKICAgIGxlZ2VuZF90aXRsZSA9ICJDb3NpbmUgc2ltaWxhcml0eSIsCiAgICBncmlkX2xpbmVzID0gVCwKICAgIGRlbmRyb2dyYW1zID0gVCwKICAgIGxlZ2VuZF9vcmllbnRhdGlvbiA9ICJob3Jpem9udGFsIiwKICAgIGxlZ2VuZF9sZW5ndGggPSB1bml0KDIsICJjbSIpLAogICAgbWF0cml4X25hbWVzX3NpemUgPSA2LCAKICAgIGxlZ2VuZF90aXRsZV9zaXplID0gNy41LAogICAgbGVnZW5kX2xhYmVsX3NpemUgPSA2LAogICAgZGVuZHJvZ3JhbV93ZWlnaHQgPSB1bml0KDIsICJtbSIpCiAgKQoKCnBsdF9maWcgPC0gY293cGxvdDo6cGxvdF9ncmlkKAogIE5VTEwsIAogIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICAgIE5VTEwsIHBsdF9ncmFwaF9pY2QsIG5yb3c9MSwgcmVsX3dpZHRocz1jKDAuMDUsMC45KQogICksCiAgTlVMTCwKICBjb3dwbG90OjpwbG90X2dyaWQoCiAgICBjb3dwbG90OjpwbG90X2dyaWQoTlVMTCwgcGx0X2VkZ2VfaWNkICxyZWxfaGVpZ2h0cyA9IGMoMC4xLCAxKSwgbmNvbCA9IDEpLAogICAgTlVMTCwKICAgIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICAgICAgTlVMTCwKICAgICAgZ3JpZDo6Z3JpZC5ncmFiRXhwcihDb21wbGV4SGVhdG1hcDo6ZHJhdyhwbHRfaGVhdG1hcF9pY2QsIGhlYXRtYXBfbGVnZW5kX3NpZGUgPSAiYm90dG9tIikpLAogICAgICBOVUxMLAogICAgICBuY29sID0gMSwKICAgICAgcmVsX2hlaWdodHMgPSBjKDAuMSwgMSwgMC4xKQogICAgKSwKICAgIG5yb3cgPSAxLAogICAgcmVsX3dpZHRocyA9IGMoMC41LCAwLjEsIDAuNiksCiAgICBsYWJlbHMgPSBjKCJCIiwiIiwiQyIpLAogICAgYXhpcyA9ICdoJywgYWxpZ24gPSAnYnQnCiAgKSwKCiAgbmNvbCA9IDEsCiAgcmVsX2hlaWdodHMgPSBjKDAuMDUsIDAuOTUsIDAuMDEsIDEpLAogIGxhYmVscyA9IGMoIkEiKQopCgpwbHRfZmlnIApgYGAKCiMjIyBWZXJzaW9uIDIKYGBge3IsIGZpZy53aWR0aD0zLjUsIGZpZy5oZWlnaHQ9NS41fQojIG5fZGlhZ25vc2VzX2JhciA8LSAxMAojIG5fZGlhZ25vc2VzX2FidW5kYW5jZSA8LSA1MAojIG5fZGlhZ25vc2VzX2N1bXVsYXRpdmUgPC0gNTAKCnRpdGxlX3NpemUgPC0gOQpsYWJlbF9zaXplIDwtIDYKbGVnZW5kX3hfcGFkIDwtIDAKbGVnZW5kX3lfcGFkIDwtIDAKCmFwcGx5X3RleHRfZm9ybWF0dGluZyA8LSBsaXN0KAogIHRoZW1lKAogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IHRpdGxlX3NpemUpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IGxhYmVsX3NpemUsIG1hcmdpbj1tYXJnaW4oMCwwLDAscj0xKSksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSBsYWJlbF9zaXplICsgMSksCiAgICBsZWdlbmQua2V5LmhlaWdodCA9IHVuaXQoMC40LCAnY20nKSwKICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDAuNCwgJ2NtJyksCiAgICAjIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3Qoc2l6ZSA9ICBtYXJnaW4oMCwwLDAsMCkpLAogICAgbGVnZW5kLmJveC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDEpLAogICAgbGVnZW5kLm1hcmdpbiA9IG1hcmdpbigKICAgICAgdCA9IGxlZ2VuZF95X3BhZCwKICAgICAgciA9IGxlZ2VuZF94X3BhZCsyLAogICAgICBiID0gbGVnZW5kX3lfcGFkLAogICAgICBsID0gbGVnZW5kX3hfcGFkIAogICAgKSwKICAgIGxlZ2VuZC5rZXkuc3BhY2luZy55ID0gdW5pdCgtMS41LCAicHQiKSwKICAgIGxlZ2VuZC5ib3guc3BhY2luZyA9IHVuaXQoMS4wLCJwdCIpLAogICAgYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCB2anVzdD0wLjUpCiAgKQopCgoKc3RyaXBfbWFyZ2luIDwtIDEKc3RyaXBfZm9ybWF0dGluZyA8LSBsaXN0KHRoZW1lKAogIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpLCAKICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSBzdHJpcF9tYXJnaW4sIHIgPSBzdHJpcF9tYXJnaW4sIGIgPSBzdHJpcF9tYXJnaW4sIGwgPSBzdHJpcF9tYXJnaW4pKQogICMgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChtYXJnaW4gPSBtYXJnaW4odCA9IHN0cmlwX21hcmdpbiwgciA9IHN0cmlwX21hcmdpbiwgYiA9IHN0cmlwX21hcmdpbiwgbCA9IHN0cmlwX21hcmdpbikpCikpCgpzZXQuc2VlZCgxMjM0KQpwbHRfZ3JhcGhfaWNkIDwtCiAgbXVsdGlfbWFrZV9jb2RpYWdub3Npc19ncmFwaCgKICAgIHRocmVzaG9sZF9tZXRob2QgPSAiYXZlcmFnZSIsCiAgICB0b3BfbiA9IDEwMCwKICAgIGxheW91dCA9ICJzdHJlc3MiLAogICAgZGZfZ3B0My41X2NvZGlhZywKICAgIGRmX2dwdDQuMF9jb2RpYWcsCiAgICBkZl9jbGF1ZGUzX2hhaWt1X3QxLjBfY29kaWFnLAogICAgZGZfY2xhdWRlM19vcHVzX3QxLjBfY29kaWFnLAogICAgZGZfZ2VtaW5pMS4wX3Byb190MS4wX2NvZGlhZywKICAgIGRmX2dlbWluaTEuNV9wcm9fdDEuMF9jb2RpYWcsCiAgICBwb2ludF9zaXplID0gMS4yNSwKICAgIGJvcmRlcl9zaXplID0gMC4yNSwKICAgIGVkZ2Vfd2lkdGggPSAwLjUsCiAgICBlZGdlX2FscGhhID0gMC41LAogICAgbGFiZWxfdGV4dF9zaXplID0gOSwKICAgIHRpY2tfdGV4dF9zaXplID0gNiwKICAgIGhpZ2hsaWdodF9zdHJva2VfbXVsdGlwbGllciA9IDMsCiAgICBsZWdlbmRfaGVpZ2h0ID0gdW5pdCgyNSwgInB0IiksCiAgICBsZWdlbmRfd2lkdGggPSB1bml0KDEwLCAicHQiKQogICkgCgpwbHRfZWRnZV9pY2QgPC0gbXVsdGlfZWRnZV9kZW5zaXR5X3Bsb3QoCiAgZGZfZ3B0My41X2ljZF9jb2RpYWcsCiAgZGZfZ3B0NC4wX2ljZF9jb2RpYWcsCiAgZGZfY2xhdWRlM19oYWlrdV90MS4wX2ljZF9jb2RpYWcsCiAgZGZfY2xhdWRlM19vcHVzX3QxLjBfaWNkX2NvZGlhZywKICBkZl9nZW1pbmkxLjBfcHJvX3QxLjBfaWNkX2NvZGlhZywKICBkZl9nZW1pbmkxLjVfcHJvX3QxLjBfaWNkX2NvZGlhZwopICArCiAgYXBwbHlfdGV4dF9mb3JtYXR0aW5nICsKICAjIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgZ3VpZGVzKGNvbG9yID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDEpLCBuY29sID0gMikpKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4xLDAuMSkpKQoKcGx0X2Nvc2luZSA8LSBjb3NpbmVfc2ltaWxhcml0eV9jb21wYXJlKGRmX2NvbXAsIHBfc2l6ZSA9IDIpICsKICBhcHBseV90ZXh0X2Zvcm1hdHRpbmcKCnBsdF9oZWF0bWFwX2ljZCA8LQogIGN1c3RvbV9oZWF0bWFwKAogICAgYXZlcmFnZV9jb3NpbmVfbWF0cml4LAogICAgc3ltbWV0cmljID0gRiwKICAgIGxlZ2VuZF90aXRsZSA9ICJBdmVyYWdlXG5jb3NpbmUgc2ltaWxhcml0eSIsCiAgICBncmlkX2xpbmVzID0gVCwKICAgIGRlbmRyb2dyYW1zID0gVCwKICAgIGxlZ2VuZF9vcmllbnRhdGlvbiA9ICJob3Jpem9udGFsIiwKICAgIGxlZ2VuZF9sZW5ndGggPSB1bml0KDIuNSwgImNtIiksCiAgICBtYXRyaXhfbmFtZXNfc2l6ZSA9IDYsIAogICAgbGVnZW5kX3RpdGxlX3NpemUgPSA3LjUsCiAgICBsZWdlbmRfbGFiZWxfc2l6ZSA9IDYsCiAgICBkZW5kcm9ncmFtX3dlaWdodCA9IHVuaXQoMi41LCAibW0iKQogICkKCnBkIDwtIC0xCgpwbHRfZmlnIDwtIGNvd3Bsb3Q6OnBsb3RfZ3JpZCgKICAjMQogIE5VTEwsIAogICMyCiAgY293cGxvdDo6cGxvdF9ncmlkKAogICAgTlVMTCwgcGx0X2dyYXBoX2ljZCwgbnJvdz0xLCByZWxfd2lkdGhzPWMoMC4wNSwwLjkpCiAgKSwKICAjMwogIE5VTEwsCiAgIzQKICBjb3dwbG90OjpwbG90X2dyaWQoCiAgICAjNC4xCiAgICBjb3dwbG90OjpwbG90X2dyaWQoCiAgICAgICM0LjEuMQogICAgICBwbG90X2dyaWQoCiAgICAgICAgIzQuMS4xLjEKICAgICAgICBwbHRfZWRnZV9pY2QrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAjNC4xLjEuMgogICAgICAgIHBsdF9jb3NpbmUrdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikreWxpbSgwLjQsMSksIAogICAgICAgICMgcmVsX3dpZHRocyA9IGMoMTAsOSksCiAgICAgICAgbnJvdz0xLAogICAgICAgIGFsaWduPSJoIixheGlzPSJidCIKICAgICAgICApLAogICAgICAjNC4xLjIKICAgICAgTkEsCiAgICAgIHBsb3RfZ3JpZChOVUxMLGdldF9sZWdlbmQocGx0X2VkZ2VfaWNkKSxucm93PTEscmVsX3dpZHRocz1jKDAuMTUsMSkpLAogICAgICBOQSwKICAgICAgbmNvbD0xLAogICAgICByZWxfaGVpZ2h0cyA9IGMoMSwgMC4wNCwgMC4xLDAuMSkgIzQuMS5fCiAgICApLAogICAgIzQuMgogICAgY293cGxvdDo6cGxvdF9ncmlkKAogICAgICAjNC4yLjEKICAgICAgTlVMTCwKICAgICAgIzQuMi4yCiAgICAgIGdyaWQ6OmdyaWQuZ3JhYkV4cHIoQ29tcGxleEhlYXRtYXA6OmRyYXcocGx0X2hlYXRtYXBfaWNkLCBoZWF0bWFwX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIsIHBhZGRpbmcgPSB1bml0KGMoMCwwLDAsLTEpLCAibW0iKSkpLAogICAgICAjNC4yLjMKICAgICAgTlVMTCwKICAgICAgbmNvbCA9IDEsCiAgICAgIHJlbF9oZWlnaHRzID0gYygwLjEsIDEsIDAuMSkKICAgICksCiAgICBucm93ID0gMSwKICAgIHJlbF93aWR0aHMgPSBjKDAuNiwgIDAuNSksICM0Ll8KICAgIGFsaWduID0gJ2gnLCBheGlzID0gJ2J0JwogICksCgogIG5jb2wgPSAxLAogIHJlbF9oZWlnaHRzID0gYygwLjA1LCAwLjk1LCAwLjA1LCAxKQopCgpwbHRfZmlnIDwtIGNvd3Bsb3Q6OmdnZHJhdyhwbHRfZmlnKStjb3dwbG90OjpkcmF3X3Bsb3RfbGFiZWwoYygiQSIsIkIiLCJDIiwiRCIpLCB4PWMoMCwwLDAuMjUsMC41MiksIHk9YygxLHJlcCgwLjUyLDMpKSkKcGx0X2ZpZyAKYGBgCgoKYGBge3IsIGV2YWw9Rn0KZ2dzYXZlKGhlcmUoImZpZ3VyZXMvNF9OZXR3b3JrX2FuYWx5c2lzLnBkZiIpLCBwbG90PXBsdF9maWcsIGhlaWdodCA9IDUuNSwgd2lkdGggPSAzLjUpCmBgYAoK